/*
 * Copyright 2014 Jens K., Jalbum AB
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.package net.jalbum.jprint;
 */
package net.jalbum.areatagging;

import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import se.datadosen.component.JBackgroundPanel;

/**
 * This interactive selector allows the manipulation of area tags within an
 * image.
 */
public class JAreaTaggingSelector extends JBackgroundPanel {

    private final JAreaTaggingDialog dialog;                        // dialog containing this area tagging selector

    private MouseState mouseState = MouseState.OUTSIDE;
    private Point plantPoint;                                       // point for evaluating movements
    private boolean newTag = false;                                 // tag creation mode on and off switch

    public JAreaTaggingSelector(JAreaTaggingDialog dialog) {
        this.dialog = dialog;

        init();
    }

    @Override
    protected void paintComponent(Graphics g2d) {
        Graphics2D g = (Graphics2D) g2d;
        super.paintComponent(g);

        if (bg != null) {
            Rectangle bounds = getImageBounds();
            Dimension sDim = bounds.getSize();
            g.translate(bounds.x, bounds.y);

            // draw inactive components
            for (AreaTag tag : dialog.getAreaTags()) {

                if (tag != dialog.getSelectedTag()) {
                    tag.drawInactive(g, sDim);
                } else {
                    tag.drawActive(g, sDim, dialog.getNameFieldText());
                }
            }
        }
    }

    private void init() {
        MouseAdapter ma = new MouseAdapter() {

            @Override
            public void mouseDragged(MouseEvent e) {
                // just do something if left mouse button is used
                if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == 0) {
                    return;
                }

                // image bounds inside BackgroundPanel
                Rectangle bounds = getImageBounds();
                Dimension sDim = bounds.getSize();
                Point p = convertComponentToImagePosition(bounds, e.getPoint());

                // if over inactive tag just select it and skip the rest
                if (mouseState.equals(MouseState.OVER_INACTIVE)) {
                    switchToTag(e.getPoint());
                    return;
                }

                // create new tag if outside existing tags and not already creating a new tag
                if (mouseState.equals(MouseState.OUTSIDE) && !newTag) {
                    dialog.addAreaTag(p, new Dimension(bounds.width, bounds.height));
                    newTag = true;
                    return;
                }

                // calculate changes of x and y
                e.translatePoint(-plantPoint.x, -plantPoint.y);
                p = e.getPoint();

                // if new tag created, check in which direktion the user resizes
                if (newTag) {
                    newTag = false;

                    if ((p.x <= 0) && (p.y <= 0)) {
                        mouseState = MouseState.NW;
                    } else if ((p.x >= 0) && (p.y <= 0)) {
                        mouseState = MouseState.NE;
                    } else if ((p.x <= 0) && (p.y >= 0)) {
                        mouseState = MouseState.SW;
                    } else if ((p.x >= 0) && (p.y >= 0)) {
                        mouseState = MouseState.SE;
                    }
                }

                // resize or move area tag
                AreaTag manipulatedTag = dialog.getSelectedTag();
                int cursor = manipulatedTag.resizeOrMove(mouseState, sDim, p);

                // update cursor and mouse location
                setCursor(Cursor.getPredefinedCursor(cursor));
                if (cursor == Cursor.NW_RESIZE_CURSOR) {
                    mouseState = MouseState.NW;
                } else if (cursor == Cursor.NE_RESIZE_CURSOR) {
                    mouseState = MouseState.NE;
                } else if (cursor == Cursor.SW_RESIZE_CURSOR) {
                    mouseState = MouseState.SW;
                } else if (cursor == Cursor.SE_RESIZE_CURSOR) {
                    mouseState = MouseState.SE;
                }

                // move plant point for next pass
                plantPoint.translate(p.x, p.y);
                repaint();
            }

            @Override
            public void mousePressed(MouseEvent e) {
                plantPoint = e.getPoint();
                requestFocusInWindow();

                if (mouseState == MouseState.OVER_INACTIVE) {
                    switchToTag(e.getPoint());
                }

                showPopupIfSelected(e);

                if (mouseState == MouseState.OUTSIDE) {
                    dialog.setSelectedTag(null);
                }
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                showPopupIfSelected(e);

            }

            @Override
            public void mouseClicked(MouseEvent e) {
                switchToNameField(e);
            }

            @Override
            public void mouseMoved(MouseEvent e) {
                showToolTip(e.getPoint());
                updateMouseState(e.getPoint());
            }

            private void showPopupIfSelected(MouseEvent e) {
                if (dialog.getSelectedTag() != null && e.isPopupTrigger()) {
                    dialog.showPopup(e.getPoint());
                }
            }

            private void switchToNameField(MouseEvent e) {
                Rectangle bounds = getImageBounds();
                Dimension sDim = bounds.getSize();
                Graphics2D g = (Graphics2D) getGraphics();

                // get click position
                Point p = convertComponentToImagePosition(bounds, e.getPoint());

                //check if point is inside of any tag
                for (AreaTag tag : dialog.getAreaTags()) {
                    if ((tag.isPointInsideName(g, sDim, p)) && (e.getClickCount() == 2)) {
                        dialog.moveFocusToTextfield();
                    }
                }
            }
        };
        addMouseListener(ma);
        addMouseMotionListener(ma);
    }

    private void switchToTag(Point p) {
        Rectangle bounds = getImageBounds();
        Dimension sDim = bounds.getSize();
        Graphics2D g = (Graphics2D) getGraphics();

        // get click position
        p = convertComponentToImagePosition(bounds, p);

        //check if point is inside of any tag
        for (AreaTag tag : dialog.getAreaTags()) {

            if (tag.isPointInsideTag(g, sDim, p) || tag.isPointInsideName(g, sDim, p)) {
                // set tag
                dialog.setSelectedTag(tag);
                setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
                mouseState = MouseState.OVER_ACTIVE;

                // stop if tag has been found¨
                return;
            }
        }

        // if no tag exists at this position
        dialog.setSelectedTag(null);
        mouseState = MouseState.OUTSIDE;

    }

    private void showToolTip(Point p) {
        Rectangle bounds = getImageBounds();
        Dimension sDim = bounds.getSize();

        Graphics2D g = (Graphics2D) getGraphics();
        p = convertComponentToImagePosition(bounds, p);

        //check if point is inside of any tag
        for (AreaTag tag : dialog.getAreaTags()) {
            if (tag.isPointInsideTag(g, sDim, p)) {
                String tipText;

                // setup tooltip text
                if (tag == dialog.getSelectedTag()) {
                    tipText = dialog.getDescriptionFieldText();
                } else {
                    tipText = tag.getDescription();
                }

                // show tooltip
                if (tipText != null && !tipText.isEmpty()) {
                    this.setToolTipText(tipText);
                }

                // stop if tag has been found
                return;
            }
        }

        // if no tag exists at this position
        this.setToolTipText(null);
    }

    private void updateMouseState(Point p) {
        Rectangle bounds = getImageBounds();
        Dimension sDim = bounds.getSize();
        Graphics2D g = (Graphics2D) getGraphics();

        p = convertComponentToImagePosition(bounds, p);

        // if a tag is active, update based on active tag
        AreaTag selected = dialog.getSelectedTag();
        if (selected != null) {
            mouseState = selected.updateMouseState(g, sDim, p);

            // update cursor
            setCursor(mouseState.getCursor());
        }

        // update based on inactive tags, if not over active one
        if (getCursor() == Cursor.getDefaultCursor()) {
            for (AreaTag tag : dialog.getAreaTags()) {

                // don't check active tag again
                if (tag != dialog.getSelectedTag()) {
                    if (tag.isPointInsideTag(g, sDim, p) || tag.isPointInsideName(g, sDim, p)) {
                        mouseState = MouseState.OVER_INACTIVE;
                    }
                }
            }
        }
    }

    private Point convertComponentToImagePosition(Rectangle bounds, Point p) {
        int xCorrection = ((getWidth() - bounds.width) / 2);
        int yCorrection = ((getHeight() - bounds.height) / 2);
        p.x = p.x - xCorrection;
        p.y = p.y - yCorrection;
        return p;
    }

    public void resetCursor() {
        mouseState = MouseState.OUTSIDE;
        setCursor(mouseState.getCursor());
    }
}
